

#ifndef __ZLIB_COMPRESS_H_
#define __ZLIB_COMPRESS_H_
////////////////////////////////////////////////////////////////////////////////
#if defined(OS_WINDOWS)
#ifndef ZLIB_WINAPI
#define ZLIB_WINAPI
#endif
#endif
////////////////////////////////////////////////////////////////////////////////
#include <zlib.h>
#include <string>
#include <asio++/http/algo/base64.h>
////////////////////////////////////////////////////////////////////////////////
inline std::string zip_encode(const char *data, size_t bytes, bool base64_encoded = false){
    char buffer[2][8192];
    std::string compressed;
    char  *buffer_encoded = buffer[1];
    Bytef *buffer_compressed = (Bytef*)buffer[0];
    for (int i = 0; i < 1; i++){
        uLongf bytes_compressed = compressBound((uLong)bytes);
        if (bytes_compressed > sizeof(buffer[0])){
            buffer_compressed = new Bytef[bytes_compressed];
        }
        if (!buffer_compressed){
            break;
        }
        int result = ::compress(buffer_compressed, &bytes_compressed, (Bytef*)data, (uLong)bytes);
        if (result != Z_OK){
            break;
        }
        if (!base64_encoded){
            compressed.append((char*)buffer_compressed, bytes_compressed);
            break;
        }
        uLongf bytes_encoded = bytes_compressed + bytes_compressed / 2;
        if (bytes_encoded > sizeof(buffer[1])){
            buffer_encoded = new char[bytes_encoded];
        }
        if (!buffer_encoded){
            break;
        }
        base64::encode(buffer_compressed, buffer_encoded, bytes_compressed);
        compressed.append(buffer_encoded, strlen(buffer_encoded));
    }
    if (buffer_encoded && buffer_encoded != buffer[1]){
        delete[] buffer_encoded;
    }
    if (buffer_compressed && buffer_compressed != (Bytef*)buffer[0]){
        delete[] buffer_compressed;
    }
    return std::move(compressed);
}
////////////////////////////////////////////////////////////////////////////////
inline std::string zip_encode(const std::string &data, bool base64_encoded = false){
    return zip_encode(data.c_str(), data.size(), base64_encoded);
}
////////////////////////////////////////////////////////////////////////////////
inline std::string zip_decode(const char *data, size_t bytes, bool base64_encoded = false){
    char buffer[2][8192];
    std::string uncompressed;
    char  *buffer_decoded = buffer[1];
    Bytef *buffer_uncompressed = (Bytef*)buffer[0];
    for (int i = 0; i < 1; i++){
        uLongf bytes_decoded = (uLong)bytes;
        if (bytes_decoded > sizeof(buffer[1])){
            buffer_decoded = new char[bytes_decoded];
        }
        if (!buffer_decoded){
            break;
        }
        if (base64_encoded){
            bytes_decoded = base64::decode(data, (unsigned char*)buffer_decoded);
        } else {
            bytes_decoded = (uLong)bytes;
            memcpy(buffer_decoded, data, bytes_decoded);
        }
        uLongf bytes_uncompressed = (uLongf)sizeof(buffer[0]);
        if (bytes_uncompressed < bytes_decoded * 2) {
            bytes_uncompressed = bytes_decoded * 2;
            buffer_uncompressed = new Bytef[bytes_uncompressed];
        }
        while (true){
            int result = ::uncompress(buffer_uncompressed, &bytes_uncompressed, (Bytef*)buffer_decoded, bytes_decoded);
            if (result == Z_OK) {
                uncompressed.append((char*)buffer_uncompressed, bytes_uncompressed);
                break;
            }
            if (result != Z_BUF_ERROR){
                break;
            }
            bytes_uncompressed <<= 1;
            if (buffer_uncompressed != (Bytef*)buffer[0]){
                delete[] buffer_uncompressed;
            }
            buffer_uncompressed = new Bytef[bytes_uncompressed];
            if (!buffer_uncompressed){
                break;
            }
        }
    }
    if (buffer_decoded && buffer_decoded != buffer[1]){
        delete[] buffer_decoded;
    }
    if (buffer_uncompressed && buffer_uncompressed != (Bytef*)buffer[0]){
        delete[] buffer_uncompressed;
    }
    return std::move(uncompressed);
}
////////////////////////////////////////////////////////////////////////////////
inline std::string zip_decode(const std::string &data, bool base64_encoded = false){
    return zip_decode(data.c_str(), data.size(), base64_encoded);
}
////////////////////////////////////////////////////////////////////////////////
#endif //__ZLIB_COMPRESS_H_
